/*
 *
 * Copyright (c) 2023 - future
 * @author fzh
 * @email e_341877120_mail@163.com
 * @link https://mvnrepository.com/search?q=io.github.xrfzh.cn
 *
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package cn.xrfzh.system.service.impl;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import cn.xrfzh.system.common.base.MyServiceImpl;
import cn.xrfzh.system.common.constants.RegularConstant;
import cn.xrfzh.system.common.constants.SystemConstant;
import cn.xrfzh.system.common.enums.ResultEnum;
import cn.xrfzh.system.common.exception.ServiceException;
import cn.xrfzh.system.common.utils.CurrentLoginUserUtil;
import cn.xrfzh.system.entity.user.AdminMenuVo;
import cn.xrfzh.system.entity.user.AssociatedRoleDto;
import cn.xrfzh.system.entity.user.PasswordDto;
import cn.xrfzh.system.entity.user.User;
import cn.xrfzh.system.mapper.UserMapper;
import cn.xrfzh.system.service.RoleService;
import cn.xrfzh.system.service.UserService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 总后台用户表(User)表服务实现类
 */
@Service("userService")
public class UserServiceImpl extends MyServiceImpl<UserMapper, User> implements UserService {

	@Resource
	private RoleService roleService;

	/**
	 * 获取当前登录用户的id与用户名
	 * @return Map<String, Object>
	 */
	public Map<String, Object> getUserInfo() {
		Long userId = CurrentLoginUserUtil.getUserId();
		User user = getById(userId);
		String username = user.getName();
		Map<String, Object> map = new HashMap<>();
		map.put("userId", userId);
		map.put("userName", username);
		return map;
	}

	/**
	 * 修改当前登录人密码
	 * @param passwordDTO
	 * @return
	 */
	@Override
	public boolean setPassword(PasswordDto passwordDTO) {
		Long userId = CurrentLoginUserUtil.getUserId();
		User user = baseMapper.selectById(userId);
		if (!StrUtil.equals(DigestUtil.bcrypt(passwordDTO.getOldPassword()), user.getPassword())) {
			throw new ServiceException("旧密码不正确！");
		}
		else {
			user.setPassword((DigestUtil.bcrypt(passwordDTO.getNewPassword())));
			return SqlHelper.retBool(baseMapper.updateById(user));
		}
	}

	/**
	 * 用户指定角色
	 * @param associatedRoleDTO
	 * @return
	 */
	@Override
	public boolean associatedRole(AssociatedRoleDto associatedRoleDTO) {
		baseMapper.removeAssociatedRole(associatedRoleDTO);
		boolean res = SqlHelper.retBool(baseMapper.associatedRole(associatedRoleDTO));
		if (res) {
			// 删除该用户登录的角色权限
			SaSession session = StpUtil.getSessionByLoginId(associatedRoleDTO.getAccId(), false);
			if (null != session) {
				session.delete("Role_List");
			}
		}
		return res;
	}

	/**
	 * 启禁用账户
	 * @param accountId 账户id
	 */
	@Override
	public boolean disOrEnaAccount(Long accountId) {
		User user = new User();
		LambdaUpdateWrapper<User> updateWrapper = Wrappers.lambdaUpdate();
		updateWrapper.eq(User::getInitial, false).setSql("enable = not enable").eq(User::getId, accountId);
		return SqlHelper.retBool(baseMapper.update(user, updateWrapper));
	}

	/**
	 * 获取当前登录人菜单
	 */
	@Override
	public List<AdminMenuVo> getMenuList() {
		List<AdminMenuVo> menuList = baseMapper.getMenuListByUserId(CurrentLoginUserUtil.getUserId());
		if (menuList.size() == 0) {
			return Collections.emptyList();
		}
		Map<Long, List<AdminMenuVo>> collect = menuList.stream()
				.collect(Collectors.groupingBy(AdminMenuVo::getParentId));
		List<AdminMenuVo> parent = collect.get(0L);
		parseList(parent, collect);
		return parent;
	}

	/**
	 * 递归拼接菜单
	 */
	private void parseList(List<AdminMenuVo> parent, Map<Long, List<AdminMenuVo>> permissions) {
		for (AdminMenuVo adminMenuVO : parent) {
			Long id = adminMenuVO.getPermissionId();
			List<AdminMenuVo> children = permissions.get(id);
			if (CollectionUtil.isNotEmpty(children)) {
				adminMenuVO.setSubMenu(children);
				parseList(children, permissions);
			}
		}
	}

	/**
	 * 新增数据
	 * @param user
	 * @return
	 */
	@Override
	public boolean insert(User user) {
		LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery();
		queryWrapper.eq(User::getAccount, user.getAccount());
		User exist = baseMapper.selectOne(queryWrapper);
		if (exist != null) {
			throw new ServiceException("当前用户名已存在！");
		}
		user.setPassword(DigestUtil.bcrypt(user.getPassword()));
		user.setEnable(true);
		user.setInitial(false);
		int insert = baseMapper.insert(user);
		// 添加默认角色
		List<Object> roleIds = roleService.listDefaultRole();
		if (roleIds != null && roleIds.size() > 0) {
			baseMapper.associatedRole(new AssociatedRoleDto(user.getId(), roleIds));
		}
		return SqlHelper.retBool(insert);
	}

	/**
	 * 修改数据
	 * @param user
	 * @return
	 */
	@Override
	public boolean update(User user) {
		// 禁止修改账户名
		user.setAccount(null);
		String password = user.getPassword();
		if (StrUtil.isNotBlank(password)) {
			boolean match = ReUtil.isMatch(RegularConstant.PASSWORD, password);
			if (!match) {
				throw new ServiceException(RegularConstant.PASSWORD_MASSAGE, ResultEnum.PARAMETER_ERROR);
			}
			user.setPassword(DigestUtil.bcrypt(password));
		}
		return SqlHelper.retBool(baseMapper.updateById(user));
	}

	/**
	 * 批量设置内置账户
	 * @param idList 账户id
	 */
	@Override
	public boolean setInitAccount(List<Long> idList) {
		if (idList.size() > 0) {
			// 去除所有内置账户
			LambdaUpdateWrapper<User> updateWrapper = Wrappers.lambdaUpdate();
			updateWrapper.eq(User::getInitial, true).set(User::getInitial, false);
			User entity = new User();
			baseMapper.update(entity, updateWrapper);
			// 重新分配内置账户
			LambdaUpdateWrapper<User> updateInitWrapper = Wrappers.lambdaUpdate();
			updateInitWrapper.in(User::getId, idList).set(User::getInitial, true);
			return SqlHelper.retBool(baseMapper.update(entity, updateInitWrapper));
		}
		return false;
	}

	/**
	 * 获取验证码
	 * @param response
	 */
	@Override
	public void getVerification(HttpServletResponse response) {
		// 定义图形验证码的长、宽、验证码字符数、干扰元素个数
		LineCaptcha captcha = CaptchaUtil.createLineCaptcha(100, 32, 4, 4);
		try (ServletOutputStream outputStream = response.getOutputStream()) {
			Cookie cookie = new Cookie("verificationCode", captcha.getCode());
			cookie.setPath("/");
			cookie.setMaxAge(1800);
			response.addCookie(cookie);
			SaSession tokenSession = StpUtil.getAnonTokenSession();
			tokenSession.delete(SystemConstant.CAPTCHA_SESSION_KEY);
			tokenSession.set(SystemConstant.CAPTCHA_SESSION_KEY, captcha.getCode());
			// 图形验证码写出，可以写出到文件，也可以写出到流
			captcha.write(outputStream);
			outputStream.flush();
		}
		catch (IOException e) {
			log.error(e.getMessage());
		}
	}

	/**
	 * 通过userId 获取权限code
	 * @param id 用户id
	 * @return 权限code
	 */
	@Override
	public List<String> roleCodeByUserId(Long id) {
		return baseMapper.selectRoleCodeByUserId(id);
	}

}
